package com.etone.smartAudit.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.etone.smartAudit.domain.AccountInfo;
import com.etone.smartAudit.domain.IapDataMeta;
import com.etone.smartAudit.domain.IapDataModel;
import com.etone.smartAudit.domain.IapDataModelDict;
import com.etone.smartAudit.dto.iapDataModel.IapDataModelDTO;
import com.etone.smartAudit.enums.AccountType;
import com.etone.smartAudit.enums.DataBaseType;
import com.etone.smartAudit.enums.StateType;
import com.etone.smartAudit.enums.UseState;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.IapDataModelMapper;
import com.etone.smartAudit.mapper.IapPlatformMapper;
import com.etone.smartAudit.mapstruct.IapDataModelMapstruct;
import com.etone.smartAudit.utils.Excel.IapDataModelExcelUtil;
import com.etone.smartAudit.utils.Excel.ImportErrorInfo;
import com.etone.smartAudit.utils.StringUtil;
import com.etone.smartAudit.utils.database.DatabaseUtil;
import com.etone.smartAudit.vo.FileVO;
import com.etone.smartAudit.vo.PagerVO;
import com.etone.smartAudit.vo.dataModelType.DataModelTypeTreeVO;
import com.etone.smartAudit.vo.iapDataModel.IapDataModelBaseVO;
import com.etone.smartAudit.vo.iapDataModel.IapDataModelDetailVO;
import com.etone.smartAudit.vo.iapDataModel.IapDateModelDictVO;
import com.etone.smartAudit.vo.iapPlatform.IapPlatformSimpleVO;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: sovy
 * @date: 2020/8/27
 * @description:
 */

@Service
@AllArgsConstructor
public class IapDataModelService extends ServiceImpl<IapDataModelMapper, IapDataModel> {

    private IapDataModelMapstruct iapDataModelMapstruct;

    private FileUploadService fileUploadService;

    private IapDataMetaService iapDataMetaService;

    private IapPlatformMapper iapPlatformMapper;

    private DataModelTypeService dataModelTypeService;

    private AccountInfoService accountInfoService;

    private IapDataModelDictService iapDataModelDictService;

    /**
     * 元数据列表
     * @param iapPlatformName
     * @param cnTableName
     * @param enTableName
     * @param description
     * @param databaseName
     * @param itemId
     * @param useState
     * @param page
     * @param pageSize
     * @param senLevel
     * @param dataPssA
     * @param startTime
     * @param endTime
     * @param dataAssertIds
     * @param dataMassIds
     * @param dimensionIds
     * @param autoTagIds
     * @return
     */
    public PagerVO<IapDataModelBaseVO> list(String iapPlatformName, String cnTableName, String enTableName, String description, List<Integer> level3Ids, String databaseName, Integer itemId, UseState useState, int page, int pageSize, Integer senLevel, Integer dataPssA, Date startTime,
                                            Date endTime, List<Integer> dataAssertIds, List<Integer> dataMassIds, List<Integer> dimensionIds, List<Integer> autoTagIds) {

        Page<IapDataModelBaseVO> iapDataModelBaseVOS = PageHelper.startPage(page, pageSize).doSelectPage(() -> {
            baseMapper.findList(iapPlatformName, cnTableName, enTableName, description, level3Ids, databaseName, itemId, useState,senLevel,dataPssA,startTime,endTime,dataAssertIds,dataMassIds,dimensionIds,autoTagIds);
        });
        return PagerVO.of(iapDataModelBaseVOS);
    }

    public IapDataModel get(Integer id) {
        IapDataModel iapDataModel = baseMapper.selectById(id);
        if (iapDataModel == null) {
            throw new CustomParameterizedException("元数据不存在");
        }
        return iapDataModel;
    }

    /**
     * 新增|编辑元数据
     *
     * @param id
     * @param iapDataModelDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(Integer id, IapDataModelDTO iapDataModelDTO) {
        if (checkCnTableNameExists(id, iapDataModelDTO.getPlatformId(), iapDataModelDTO.getCnTableName())) {
            throw new CustomParameterizedException("宽表表名已存在");
        }

        if (checkEnTableNameExists(id, iapDataModelDTO.getPlatformId(), iapDataModelDTO.getEnTableName())) {
            throw new CustomParameterizedException("英文表名已存在");
        }

        IapDataModel iapDataModel;
        List<FileVO> oldDataExamples = null;
        if (id != null) {
            iapDataModel = get(id);
            oldDataExamples = StringUtils.isEmpty(iapDataModel.getDataExamples()) ? null : JSON.parseArray(iapDataModel.getDataExamples(), FileVO.class);
        } else {
            iapDataModel = IapDataModel.builder().build();
        }
        //处理标签
        String tagNames;
        if (!StringUtils.isEmpty(iapDataModelDTO.getDataAssertIds())){
            List<Integer> assertIds = StringUtil.intToList(iapDataModelDTO.getDataAssertIds());
            tagNames = "";
            for (Integer assertId : assertIds) {
                IapDataModelDict modelDict = iapDataModelDictService.get(assertId);
                tagNames += modelDict.getName() + ",";
            }
            iapDataModelDTO.setDataAssertNames(tagNames.substring(0,tagNames.length()-1));
        }

        if (!StringUtils.isEmpty(iapDataModelDTO.getDataMassIds())){
            List<Integer> massIds = StringUtil.intToList(iapDataModelDTO.getDataMassIds());
            tagNames = "";
            for (Integer massId : massIds) {
                IapDataModelDict modelDict = iapDataModelDictService.get(massId);
                tagNames += modelDict.getName() + ",";
            }
            iapDataModelDTO.setDataMassNames(tagNames.substring(0,tagNames.length()-1));
        }

        if (!StringUtils.isEmpty(iapDataModelDTO.getDimensionIds())){
            List<Integer> dimensionIds = StringUtil.intToList(iapDataModelDTO.getDimensionIds());
            tagNames = "";
            for (Integer dimensionId : dimensionIds) {
                IapDataModelDict modelDict = iapDataModelDictService.get(dimensionId);
                tagNames += modelDict.getName() + ",";
            }
            iapDataModelDTO.setDimensionNames(tagNames.substring(0,tagNames.length()-1));
        }

        if (!StringUtils.isEmpty(iapDataModelDTO.getDimensionIds())){
            List<Integer> autoTagIds = StringUtil.intToList(iapDataModelDTO.getAutoTagIds());
            tagNames = "";
            for (Integer autoTagId : autoTagIds) {
                IapDataModelDict modelDict = iapDataModelDictService.get(autoTagId);
                tagNames += modelDict.getName() + ",";
            }
            iapDataModelDTO.setAutoTagNames(tagNames.substring(0,tagNames.length()-1));
        }

        iapDataModelMapstruct.copyIapDataModel(iapDataModel, iapDataModelDTO);

        iapDataModel.setUpdateTime(new Date());
        saveOrUpdate(iapDataModel);

        //保存数据字典
        iapDataMetaService.save(iapDataModel.getId(), iapDataModelDTO.getPlatformId(), iapDataModelDTO.getIapDataMetas());

        //清理文件
        fileUploadService.deleteFileForUpdate(iapDataModelDTO.getDataExamples(), oldDataExamples);
    }

    /**
     * 校验英文表名是否存在
     *
     * @param id
     * @param platformId
     * @param enTableName
     * @return
     */
    public boolean checkEnTableNameExists(Integer id, Integer platformId, String enTableName) {
        if (StringUtils.isEmpty(enTableName)) {
            throw new CustomParameterizedException("英文表名不能为空");
        }
        if (StringUtils.isEmpty(platformId)) {
            throw new CustomParameterizedException("系统id不能为空");
        }
        return baseMapper.checkEnTableNameExists(id, platformId, enTableName);
    }

    /**
     * 校验中文表名是否存在
     *
     * @param id
     * @param platformId
     * @param cnTableName
     * @return
     */
    public boolean checkCnTableNameExists(Integer id, Integer platformId, String cnTableName) {
        if (StringUtils.isEmpty(cnTableName)) {
            throw new CustomParameterizedException("宽表表名不能为空");
        }
        if (StringUtils.isEmpty(platformId)) {
            throw new CustomParameterizedException("系统id不能为空");
        }
        return baseMapper.checkCnTableNameExists(id, platformId, cnTableName);
    }

    /**
     * 元数据详情
     *
     * @param id
     * @return
     */
    public IapDataModelDetailVO detail(Integer id) {
        IapDataModel iapDataModel = get(id);
        IapDataModelDetailVO iapDataModelDetailVO = iapDataModelMapstruct.getIapDataModelDetailVO(iapDataModel);
        return iapDataModelDetailVO;
    }

    /**
     * 通过系统平台id删除元数据
     *
     * @param iapPlatformId
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIapPlatformId(Integer iapPlatformId) {
        List<IapDataModel> iapDataModels = baseMapper.selectByIapPlatformId(iapPlatformId);
        if (!CollectionUtils.isEmpty(iapDataModels)) {
            iapDataModels.forEach(iapDataModel -> delete(iapDataModel));
        }
    }

    /**
     * 删除元数据
     *
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(Integer id) {
        IapDataModel iapDataModel = get(id);
        delete(iapDataModel);
    }

    /**
     * 删除元数据
     *
     * @param iapDataModel
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(IapDataModel iapDataModel) {
        List<FileVO> oldDataExamples = StringUtils.isEmpty(iapDataModel.getDataExamples()) ? null : JSON.parseArray(iapDataModel.getDataExamples(), FileVO.class);

        baseMapper.deleteById(iapDataModel.getId());

        //清理文件
        if (!CollectionUtils.isEmpty(oldDataExamples)) {
            fileUploadService.deleteFileByFileVO(oldDataExamples, true, true);
        }
        doAfterDelete(iapDataModel.getId());
    }

    /**
     * 批量删除元数据
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Integer> ids) {
        if (!CollectionUtils.isEmpty(ids)) {
            ids.forEach(id -> delete(id));
        }

    }

    /**
     * 删除元数据
     *
     * @param id
     */
    public void doAfterDelete(Integer id) {

        //删除数据字典
        iapDataMetaService.deleteByIapDataModelId(id);
    }

    /**
     * 导入元数据
     *
     * @param file
     */
    @Transactional(rollbackFor = Exception.class)
    public void importDataModel(MultipartFile file) {
        List<ImportErrorInfo> importErrorInfos = new ArrayList<>();

        //获取内容
        List<IapDataModel> iapDataModels = IapDataModelExcelUtil.read(file, importErrorInfos);

        //表中的系统名称
        List<String> platformNames = iapDataModels.stream().map(IapDataModel::getPlatformName).collect(Collectors.toList());
        //通过系统名称获取系统id
        List<IapPlatformSimpleVO> iapPlatformSimpleVOS = iapPlatformMapper.findByNames(platformNames);
        //系统名称map<系统名称,id>
        Map<String, Integer> iapPlatformSimpleMaps = iapPlatformSimpleVOS.stream().collect(Collectors.toMap(IapPlatformSimpleVO::getName, IapPlatformSimpleVO::getId));
        //判断系统是否存在
        platformNames.removeAll(iapPlatformSimpleVOS.stream().map(IapPlatformSimpleVO::getName).collect(Collectors.toList()));
        if (!CollectionUtils.isEmpty(platformNames)) {
            importErrorInfos.add(ImportErrorInfo.buildMessage(platformNames.stream().distinct().collect(Collectors.joining(",")) + "系统不存在"));
        }
        //判断分类是否存在
        List<DataModelTypeTreeVO> dataModelTypeTreeVOS = dataModelTypeService.listTree();

        //判断标签平台是否存在
        List<IapDateModelDictVO> iapDateModelDictVOS = iapDataModelDictService.list(null);

        //判断系统表是否存在
        for (IapDataModel iapDataModel : iapDataModels) {
            String platformName = iapDataModel.getPlatformName();
            if (!platformNames.contains(platformName) && !StringUtils.isEmpty(iapDataModel.getCnTableName())) {
                if (checkCnTableNameExists(null, iapPlatformSimpleMaps.get(platformName), iapDataModel.getCnTableName())) {
                    importErrorInfos.add(ImportErrorInfo.buildMessage(platformName + "系统中已存在" + iapDataModel.getCnTableName()));
                }
                if (checkEnTableNameExists(null, iapPlatformSimpleMaps.get(platformName), iapDataModel.getEnTableName())) {
                    importErrorInfos.add(ImportErrorInfo.buildMessage(platformName + "系统中已存在" + iapDataModel.getEnTableName()));
                }
            }
            iapDataModel.setPlatformId(iapPlatformSimpleMaps.get(platformName));

            if (StringUtils.isEmpty(iapDataModel.getLevel1())) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "一级分类不能为空"));
            }
            if (StringUtils.isEmpty(iapDataModel.getLevel2())) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "二级分类不能为空"));
            }
//            if (StringUtils.isEmpty(iapDataModel.getLevel3())) {
//                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "三级分类不能为空"));
//            }
            boolean leve1Exits = false;
            boolean leve2Exits = false;
//            boolean leve3Exits = false;
            for (DataModelTypeTreeVO dataModelTypeTreeVO : dataModelTypeTreeVOS) {
                if (dataModelTypeTreeVO.getName().equals(iapDataModel.getLevel1())) {
                    leve1Exits = true;
                    iapDataModel.setLevel1Id(dataModelTypeTreeVO.getId());
                    for (DataModelTypeTreeVO level2 : dataModelTypeTreeVO.getChildren()) {
                        if (level2.getName().equals(iapDataModel.getLevel2())) {
                            leve2Exits = true;
                            iapDataModel.setLevel2Id(level2.getId());
//                            for (DataModelTypeTreeVO level3 : level2.getChildren()) {
//                                if (level3.getName().equals(iapDataModel.getLevel3())) {
//                                    leve3Exits = true;
//                                    iapDataModel.setLevel3Id(level3.getId());
//                                    break;
//                                }
//                            }
                            break;
                        }
                    }
                    break;
                }
            }
            if (!leve1Exits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "一级分类不存在"));
            }
            if (leve1Exits && !leve2Exits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "二级分类不存在"));
            }
//            if (leve1Exits && leve2Exits && !leve3Exits) {
//                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "三级分类不存在"));
//            }

            boolean senLevelExits = false;
            boolean dataPssAExits = false;
            boolean dataAssertExits = false;
            String dataAssertIds = "";
            boolean dataMassExits = false;
            String dataMassIds = "";
            boolean dimensionExits = false;
            String dimensionIds = "";
            boolean autoTagExits = false;
            String autoTagIds = "";

            for (IapDateModelDictVO iapDateModelDictVO : iapDateModelDictVOS) {
                if (iapDateModelDictVO.getName().equals(iapDataModel.getSenLevel())){
                    senLevelExits = true;
                    iapDataModel.setSenLevelId(iapDateModelDictVO.getId());
                }
                if (iapDateModelDictVO.getName().equals(iapDataModel.getDataPssA())){
                    dataPssAExits = true;
                    iapDataModel.setDataPssAId(iapDateModelDictVO.getId());
                }
                if (iapDataModel.getDataAssertNames().contains(iapDateModelDictVO.getName())){
                    dataAssertExits = true;
                    dataAssertIds += iapDateModelDictVO.getId() + ",";
                }
                if (iapDataModel.getDataMassNames().contains(iapDateModelDictVO.getName())){
                    dataMassExits = true;
                    dataMassIds += iapDateModelDictVO.getId() + ",";
                }
                if (iapDataModel.getDimensionNames().contains(iapDateModelDictVO.getName())){
                    dimensionExits = true;
                    dimensionIds += iapDateModelDictVO.getId() + ",";
                }
                if (iapDataModel.getAutoTagNames().contains(iapDateModelDictVO.getName())){
                    autoTagExits = true;
                    autoTagIds += iapDateModelDictVO.getId() + ",";
                }
            }

            iapDataModel.setDataAssertIds(dataAssertIds);
            iapDataModel.setDataMassIds(dataMassIds);
            iapDataModel.setDimensionIds(dimensionIds);
            iapDataModel.setAutoTagIds(autoTagIds);

            if (!senLevelExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "敏感性不存在"));
            }
            if (!dataPssAExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "数据存放平台不存在"));
            }
            if (!dataAssertExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "数据维护类型不存在"));
            }
            if (!dataAssertExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "数据质量纬度不存在"));
            }
            if (!dataMassExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "数据质量纬度不存在"));
            }
            if (!dimensionExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "使用方纬度不存在"));
            }
            if (!autoTagExits) {
                importErrorInfos.add(ImportErrorInfo.buildMessage(iapDataModel.getEnTableName() + "自动化纬度不存在"));
            }

        }
        if (importErrorInfos.size() > 0) {
            String msg = importErrorInfos.stream().map(ImportErrorInfo::getMessageNoStyle).distinct().collect(Collectors.joining(","));
            throw new CustomParameterizedException(msg);
        }
        List<IapDataMeta> iapDataMetas = iapDataModels.stream().map(iapDataModel -> iapDataModel.getIapDataMetas()).flatMap(Collection::stream).collect(Collectors.toList());
        saveBatch(iapDataModels);
        iapDataModels.forEach(iapDataModel -> {
            iapDataModel.getIapDataMetas().forEach(iapDataMeta -> {
                iapDataMeta.setPlatformId(iapDataModel.getPlatformId());
                iapDataMeta.setModelId(iapDataModel.getId());
            });
        });
        iapDataMetaService.saveBatch(iapDataMetas);
    }

    /**
     * @param id
     * @param top
     * @return
     */
    public List<Map<String, Object>> metaDataTop(Integer id, Integer top) {
        IapDataModel iapDataModel = get(id);
        if (iapDataModel.getUseState().equals(UseState.USED) && iapDataModel.getServerIp() != null && iapDataModel.getDataBaseType() != null) {
            QueryWrapper<AccountInfo> queryWrapper = new QueryWrapper(
                    AccountInfo
                            .builder()
                            .serverIp(iapDataModel.getServerIp())
                            .databaseName(iapDataModel.getDatabaseName())
                            .schemaName(iapDataModel.getSchemaName())
                            .type(AccountType.valueOf(iapDataModel.getDataBaseType().name()))
                            .state(StateType.ENABLE)
                            .build());
            queryWrapper.last("limit 1");
            AccountInfo accountInfo = accountInfoService.getOne(queryWrapper);
            if (accountInfo != null) {
                String sql = String.format("select * from %s limit %s", (iapDataModel.getDataBaseType().equals(DataBaseType.GREENPLUM) ? iapDataModel.getSchemaName() + "." : "") + iapDataModel.getEnTableName(), top);
                List<Map<String, Object>> maps = DatabaseUtil.selectSql(iapDataModel.getServerIp(), accountInfo.getAccount(), accountInfo.getPassword(), accountInfo.getPort(), iapDataModel.getDatabaseName(), sql, iapDataModel.getDataBaseType());
                return maps;
            }

        }
        return null;
    }
}


